﻿/*
	Copyright (C) 2019 Zongming Liu <1849059316@qq.com>

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.
	
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	
	You should have received a copy of the GNU General Public License along
	with this program; if not, write to the Free Software Foundation, Inc.,
	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "ActionInterval.h"
#include "../node/Node.h"
#include "../base/Debuger.h"

namespace SkyEngine2d
{
	ActionInterval::ActionInterval()
		:ActionTime(0)
		,m_firstRunFlag(true)
		,m_elapsed(0)
	{
	
	}
	
	ActionInterval::~ActionInterval()
	{
	
	}
	
	bool ActionInterval::isDone() const
	{
		return m_isDone;
	}
	
	void ActionInterval::step(float dt)
	{
		if (m_firstRunFlag)
		{
			m_firstRunFlag = false;
			m_elapsed = 0;
		}
		else
		{
			m_elapsed += dt;
		}
	
		float ratio = (float)std::fmax(0, std::fmin(1, m_elapsed / m_duration));
		this->update(ratio);
	
		m_isDone = (m_elapsed > m_duration);
	}
	
	ActionMove::ActionMove()
		:m_target(nullptr)
		,m_flag(0)
	{
	
	}
	
	ActionMove::~ActionMove()
	{
	
	}
	
	ActionMoveSharedPtr ActionMove::createMoveToTargetPos(float pos_x, float pos_y, float duration)
	{
		auto p = std::make_shared<ActionMove>();
		if (p->init(pos_x,pos_y,duration,2))
		{
			return p;
		}
		return nullptr;
	}
	
	ActionMoveSharedPtr ActionMove::createMoveDistance(float distance_x, float distance_y, float duration)
	{
		auto p = std::make_shared<ActionMove>();
		if (p->init(distance_x, distance_y, duration, 1))
		{
			return p;
		}
		return nullptr;
	}
	
	bool ActionMove::init(float x, float y, float duration, int flag)
	{
		this->setDuration(duration);
		if (flag==1)
		{
			m_distance = { x,y };
		}
		else
		{
			m_endPos = { x,y };
		}
		m_flag = flag;
	
		return true;
	}
	
	void ActionMove::update(float ratio)
	{
		auto ptr = m_target;
		if (ptr)
		{
			auto cur_pos = ptr->getPosition();
			auto new_pos = m_startPos + (m_distance * ratio);
			ptr->setPosition(new_pos);
		}
	}
	
	bool ActionMove::setActionTarget(IActableObject* obj)
	{
		auto p = dynamic_cast<IMovableObject*>(obj);
		if (!p)
		{
			return false;
		}
		m_target = p;
		m_startPos = p->getPosition();
		if (m_flag != 1)
		{
			m_distance = m_endPos - m_startPos;
		}
		else
		{
			m_endPos = m_startPos + m_distance;
		}
	
		return true;
	}
	
	ActionSequenceSharedPtr ActionSequence::create(const ActionTimeSharedPtr & first, const ActionTimeSharedPtr & second)
	{
		auto p = std::unique_ptr<ActionSequence>(new ActionSequence());
		if (p && p->init(first,second))
		{
			return p;
		}
		return nullptr;
	}
	
	ActionSequenceSharedPtr ActionSequence::create(const std::vector<ActionTimeSharedPtr> & actions)
	{
		auto p = std::unique_ptr<ActionSequence>(new ActionSequence());
		if (p && p->init(actions))
		{
			return p;
		}
		return nullptr;
	}
	
	ActionSequence::~ActionSequence()
	{
	
	}
	
	bool ActionSequence::startWithTarget(IActableObject* target)
	{
		DebugerAssert(target, "执行动画的目标不能为空");
		if (target == nullptr)
		{
			return false;
		}
	
		DebugerAssert(m_actionArr[0] && m_actionArr[1], "序列动画不能为空");
		if (m_actionArr[0] == nullptr || m_actionArr[1] == nullptr)
		{
			return false;
		}
	
		if (m_duration > FLT_EPSILON)
		{
			m_split = m_actionArr[0]->getDuration() > FLT_EPSILON ? m_actionArr[0]->getDuration() / m_duration : 0;
		}
	
		if (!ActionInterval::startWithTarget(target))
		{
			return false;
		}
		
		for (auto & p:m_actionArr)
		{
			if (!p->startWithTarget(target))
			{
				return false;
			}
		}
	
		m_last = -1;
	
		return true;
	}
	
	bool ActionSequence::setActionTarget(IActableObject* obj)
	{
		m_target = obj;
		return true;
	}
	
	void ActionSequence::update(float t)
	{
		int current_index = 0;
		float new_t = 0.0f;
	
		if (t<m_split)
		{
			current_index = 0;
			if (m_split!=0)
			{
				new_t = t / m_split;
			}
			else
			{
				new_t = 1;
			}
		}
		else
		{
			current_index = 1;
			if (m_split == 1)
			{
				new_t = 1;
			}
			else
			{
				new_t = (t - m_split) / (1-m_split);
			}
		}
	
		if (current_index == 1)  //执行二个动画
		{
			if (m_last == -1) //第一个动画被跳过
			{
				m_actionArr[0]->startWithTarget(m_target);
				m_actionArr[0]->update(1.0f);
				m_actionArr[0]->stop();
			}
			else if(m_last == 0) //第一个动画执行结束 
			{
				m_actionArr[0]->update(1.0f);
				m_actionArr[0]->stop();
			}
		}
		
		if (current_index == m_last && m_actionArr[m_last]->isDone())
		{
			return;
		}
	
		if (current_index!=m_last)
		{
			m_actionArr[current_index]->startWithTarget(m_target);
		}
	
		m_actionArr[current_index]->update(new_t);
	
		m_last = current_index;
	}
	
	void ActionSequence::stop()
	{
		if (m_last!=-1&&m_actionArr[m_last])
		{
			m_actionArr[m_last]->stop();
		}
	
		ActionInterval::stop();
	}
	
	bool ActionSequence::isDone() const
	{
		return m_isDone;
	}
	
	ActionSequence::ActionSequence()
		:ActionInterval()
		, m_target(nullptr)
		, m_split(0.0f)
		, m_last(-1)
	{
	}
	
	bool ActionSequence::init(const std::vector<ActionTimeSharedPtr> & actions)
	{
		DebugerAssert(actions.size() >= 2, "序列动画至少需要提供2个动画");
	
		if (actions.size()<2)
		{
			return false;
		}
	
		auto p = actions.at(0);
		int count = actions.size();
		for (int i = 1;i< count -1 ;i++)
		{
			p = create(p, actions.at(i));
		}
	
		return init(p, actions.at(count - 1));
	}
	
	bool ActionSequence::init(const ActionTimeSharedPtr & first, const ActionTimeSharedPtr & second)
	{
		DebugerAssert(first&&second, "action 不能为空");
	
		if (!(first&&second))
		{
			return false;
		}
	
		float d = first->getDuration() + second->getDuration();
	
		ActionInterval::setDuration(d);
	
		m_actionArr[0] = first;
		m_actionArr[1] = second;
	
		return true;
	}
	
	ActionSimple::ActionSimple()
		:m_target(nullptr)
		,m_update(nullptr)
	{
	
	}
	
	ActionSimple::~ActionSimple()
	{
	
	}
	
	ActionSimpleSharedPtr ActionSimple::create(const UPDATE_CALL& update_call, float duration)
	{
		auto ptr = std::make_shared<ActionSimple>();
		if (ptr && ptr->init(update_call,duration))
		{
			return ptr;
		}
		return nullptr;
	}
	
	bool ActionSimple::init(const UPDATE_CALL& update_call, float duration)
	{
		m_update = update_call;
		m_duration = duration;
	
		return true;
	}
	
	bool ActionSimple::setActionTarget(IActableObject* obj)
	{
		m_target = obj;
		return true;
	}
	
	void ActionSimple::update(float ratio)
	{
		if (m_update)
		{
			m_update(m_target, ratio);
		}
	}
}
